/*
* Purpose: DAC driver/interface
*
* Author: Allen Lin
*
* Date: 2007/10/15
*
* Copyright Generalplus Corp. ALL RIGHTS RESERVED.
*
* Version : 1.00
* History :
*/
//Include files
#include "driver_l1.h"
#include "drv_l1_sfr.h"
#include "drv_l1_dac.h"

//=== This is for code configuration DON'T REMOVE or MODIFY it ===//
#if (defined _DRV_L1_DAC) && (_DRV_L1_DAC == 1)		//

//================================================================//
#define DAC_FEM_IF		(1 << 15)					/* DAC FIFO empty interrupt flag */
#define DAC_FEM_IEN		(1 << 14)					/* FIFO empty interrupt enable */
#define DAC_CH_EN		(1 << 13)					/* CHA,B enable */
#define DAC_VREFEN		(1 << 12)					/* DAC VREF control pin enable */
#define DAC_SIGNED		(1 << 11)					/* signed data */
#define DAC_OP_NENB		(1 << 4)					/* NENB (low active) */
#define DAC_OP_PENB		(1 << 5)					/* PENB (low active) */

#define DAC_CHB_SSF		(1 << 12)					/* CHB service frequency */
#define DAC_CHB_CHACFG	(1 << 11)					/* CHB use CHA's configuration */
#define DAC_MONO		(1 << 10)					/* Mono mode */

#define DAC_FIFO_FULL	(1 << 15)					/* FIFO full flag */
#define DAC_FIFO_RST	(1 << 8)					/* FIFO reset */
#define DAC_FIFO_LEVEL	(0xF << 4)					/* FIFO level */

#define DAC_MUTE		(1 << 8)					/* mute */
#define DAC_PGA			(0x3F << 0)					/* DAC PGA gain */

#define DAC_IIS_EN		(1 << 0)					/* IIS enable */

typedef enum
{
	DAC_ENABLED,
	DAC_DISABLED
} DAC_STATUS;

typedef enum
{
	DAC_CHA,
	DAC_CHB
} DAC_CHANNELS;

typedef struct
{
	INT16S	*cha_data;
	INT16S	*chb_data;
	INT32U	cha_len;
	INT32U	chb_len;
	INT32U	cha_count;
	INT32U	chb_count;
	INT8S	*cha_notify;
	INT8S	*chb_notify;
} DAC_CTRL;

// Constant definitions used in this file only go here
// Type definitions used in this file only go here
// Global inline functions(Marcos) used in this file only go here
// Declaration of global variables and functions defined in other files
// Variables defined in this file
INT32U fps_audio_dac_dbf_start;
INT32U fps_audio_dac_dbf_end;

DMA_STRUCT dma_struct_dbf;

DAC_CTRL dac_ctrl;

INT8U cha_int_level;
INT8U chb_int_level;

static INT32U dac_op_vol_bak = 0xF;

// Functions defined in this file
static void dac_cha_fifo_isr(void);
static void dac_chb_fifo_isr(void);

#if 0
static void drv_dac_msec_305(int cnt)
{
	int i;
	cnt = 0x2FFFF;
	for(i = 0; i < cnt; ++i)
	{
		R_RANDOM0 = i;
	}
}

#endif

void drv_l1_dac_int_set(INT8U ch, INT8U status)
{
	if (ch == DAC_CHA) {
		if (status == DAC_ENABLED) {
			R_DAC_CHA_CTRL |= DAC_FEM_IEN; /* enable FIFO empty interrupt */
		}
		else {
			R_DAC_CHA_CTRL &= ~DAC_FEM_IEN; /* disable FIFO empty interrupt */
		}
	}
	else {
		if (status == DAC_ENABLED) {
			R_DAC_CHB_CTRL |= DAC_FEM_IEN; /* enable FIFO empty interrupt */
		}
		else {
			R_DAC_CHB_CTRL &= ~DAC_FEM_IEN; /* disable FIFO empty interrupt */
		}
	}
}


void drv_l1_dac_init(void)
{
	R_SYSTEM_POWER_CTRL0 |= 0X100;		// CODEC LDO enable
	R_DAC_CHA_CTRL = (DAC_OP_NENB | DAC_OP_PENB);	 // avoid POP noise
	R_DAC_CHB_CTRL = 0;

	R_DAC_CHA_CTRL |= DAC_FEM_IF;					/* clear FIFO empty flag */
	R_DAC_CHB_CTRL |= DAC_FEM_IF;

	R_DAC_CHA_CTRL &= ~DAC_FEM_IEN;					/* disabe FIFO empty interrupt */
	R_DAC_CHB_CTRL &= ~DAC_FEM_IEN;					/* disabe FIFO empty interrupt */

	R_DAC_CHA_CTRL |= DAC_VREFEN;

	R_DAC_PGA = 0;
	R_DAC_CHA_CTRL |= (DAC_CH_EN | DAC_SIGNED);		/* enable CHA and use signed data type */
	R_DAC_CHA_CTRL &= ~DAC_OP_NENB;				  // OP_NENB	 turn on
	R_DAC_CHA_CTRL |=  DAC_OP_PENB;				  // OP_PENB	turn off
	R_DAC_CHB_CTRL |= (DAC_CH_EN | DAC_CHB_SSF);
	while((R_DAC_PGA & DAC_MUTE) != 0); /* wait vref ready */
	R_DAC_PGA = 0x10;
	R_DAC_CHA_FIFO |= DAC_FIFO_RST;					/* reset FIFO */
	R_DAC_CHB_FIFO |= DAC_FIFO_RST;

	//R_DAC_IISEN |= DAC_IIS_EN; /* enable IIS */
	vic_irq_register(VIC_AUDA, dac_cha_fifo_isr);	/* register DAC isr */
	vic_irq_enable(VIC_AUDA);

	vic_irq_register(VIC_AUDB, dac_chb_fifo_isr);	/* register DAC isr */
	vic_irq_enable(VIC_AUDB);

	dac_fifo_level_set(0x8, 0x8);
	dma_struct_dbf.channel = 0xff;

	#if DRV_l1_DAC_ON_OFF
	drv_l1_dac_int_set(DAC_CHA, DAC_ENABLED);
	#endif
}


void dac_enable_set(BOOLEAN status)
{
	if(status == TRUE)
	{
		R_DAC_CHA_CTRL |= DAC_CH_EN;
		R_DAC_CHB_CTRL |= DAC_CH_EN;
		R_DAC_CHA_CTRL &= (~0x10);					// OP_NENB     turn on
		R_DAC_CHA_CTRL |= 0x20;						// OP_PENB	turn off
		R_DAC_CHA_CTRL |= dac_op_vol_bak;			//   restore volume
		//DBG_PRINT("dac_enable_set = 0x%x\r\n",dac_op_vol_bak);
		// Enter headphone mode
	}
	else
	{
		R_DAC_CHA_CTRL &= ~DAC_CH_EN;
		R_DAC_CHB_CTRL &= ~DAC_CH_EN;
		R_DAC_CHA_CTRL |= 0x30;						// OP_NENB, OP_PENB	turn off
		R_DAC_CHA_CTRL &= ~0xF;						//   volume min
		// DAC off
	}
}

void dac_disable(void)
{
	R_DAC_CHA_CTRL &= ~DAC_VREFEN;

	/* at least delay 300 ms */
#if _OPERATING_SYSTEM == 1
	//OSTimeDly(31);
	drv_msec_wait(305);
#else
	//drv_msec_wait(305);	// josephhsieh@131122
	drv_dac_msec_305(0);
#endif
	R_ANALOG_CTRL = 0;		/* clear ENCI */
	R_DAC_CHA_CTRL &= ~DAC_CH_EN;
	R_DAC_CHB_CTRL &= ~DAC_CH_EN;
	R_DAC_CHA_CTRL |= 0x30; // OP_NENB, OP_PENB turn off
	R_DAC_CHA_CTRL &= ~0xF;
}

void dac_audop_speaker_en(BOOLEAN status)
{

	if (status)
	{
		R_DAC_CHA_CTRL |=0x0F; 
		R_DAC_CHA_CTRL &= ~0x40;
	}
	else
	{
		R_DAC_CHA_CTRL |= 0x40;
		R_DAC_CHA_CTRL &= 0xFFF0; 
	}
}

// R_DAC_CHA_CTRL[3:0]
// vol = 0x0 ~ 0xF
// step ragne: 2~4
// every step duration: 10ms ~ 100ms
void dac_audop_enable_set(void)
{
	volatile INT32U REG = R_DAC_CHA_CTRL;
	INT32U i, j;

	//R_IOB_ATT |= 0xC000;  //DEBUG
	//R_IOB_DIR |= 0xC000;  //DEBUG
	//R_IOB_O_DATA  &= (~0xC000);  //DEBUG
	// OP NENB enable
	for (i=1;i<=0xF;i+=2)		// ramp up volume (avoid pop noise)
	{
		REG = R_DAC_CHA_CTRL;
		REG &= ~0xF;
		REG |= i;
		R_DAC_CHA_CTRL = REG;
		for(j=0;j<40000;++j)		// about 23ms in CPU_CLK=225MHz
		{
			R_RANDOM0 = j;
		}
		//R_IOB_O_DATA  ^= 0xC000;  //DEBUG
	}

	// OP NENB, PENB enable
	REG &= (~0x30);
	REG |= 0xF;		// vol max
	R_DAC_CHA_CTRL = REG;
	dac_op_vol_bak = 0xF;
	//while (1) ;  //DEBUG
	// Enter speaker mode
}

void dac_audop_disable(void)
{
	R_DAC_CHA_CTRL |= 0x20;		// OP_PENB  turn off
	R_DAC_CHA_CTRL &= (~0x1F);	// OP_NENB  turn on
	R_DAC_CHA_CTRL |= dac_op_vol_bak;		// restore volume
	// Enter headphone mode
}

// vol = 0x0 ~ 0xF
void dac_audop_vol_set(int vol)
{
	volatile INT32U REG = R_DAC_CHA_CTRL;
	if(vol < 0)
		vol = 0;
	if(vol > 0xF)
		vol = 0xF;

	REG &= ~0xF;
	REG |= vol;
	R_DAC_CHA_CTRL = REG;		// op volume
	dac_op_vol_bak = vol;
}

void dac_init(void)
{
	R_DAC_CHA_CTRL = 0;
	R_DAC_CHB_CTRL = 0;

	R_DAC_CHA_CTRL |= DAC_FEM_IF; /* clear FIFO empty flag */
	R_DAC_CHB_CTRL |= DAC_FEM_IF;

	R_DAC_CHA_CTRL &= ~DAC_FEM_IEN; /* disabe FIFO empty interrupt */
	R_DAC_CHB_CTRL &= ~DAC_FEM_IEN; /* disabe FIFO empty interrupt */

	R_DAC_CHA_CTRL |= DAC_VREFEN;
	//while((R_DAC_PGA & DAC_MUTE) != 0); /* wait vref ready */

	R_DAC_CHA_CTRL |= (DAC_CH_EN|DAC_SIGNED); /* enable CHA and use signed data type */
	R_DAC_CHB_CTRL |= (DAC_CH_EN|DAC_CHB_SSF);

	R_DAC_CHA_FIFO |= DAC_FIFO_RST; /* reset FIFO */
	R_DAC_CHB_FIFO |= DAC_FIFO_RST;

	R_DAC_CHA_CTRL|= 0xf;		//set OPA max 0xf
	R_DAC_CHB_CTRL|= 0xf;

	//R_DAC_IISEN |= DAC_IIS_EN; /* enable IIS */

	vic_irq_register(VIC_AUDA, dac_cha_fifo_isr);	/* register DAC isr */
	vic_irq_enable(VIC_AUDA);

	vic_irq_register(VIC_AUDB, dac_chb_fifo_isr);	/* register DAC isr */
	vic_irq_enable(VIC_AUDB);

	dac_fifo_level_set(0x8,0x8);
	dma_struct_dbf.channel = 0xff;
}

void dac_left_init(void)
{
	R_DAC_CHA_CTRL = 0;
	R_DAC_CHB_CTRL = 0;

	R_DAC_CHA_CTRL |= DAC_FEM_IF; /* clear FIFO empty flag */
	R_DAC_CHB_CTRL |= DAC_FEM_IF;

	R_DAC_CHA_CTRL &= ~DAC_FEM_IEN; /* disabe FIFO empty interrupt */
	R_DAC_CHB_CTRL &= ~DAC_FEM_IEN; /* disabe FIFO empty interrupt */

	R_DAC_CHA_CTRL |= DAC_VREFEN;
	//while((R_DAC_PGA & DAC_MUTE) != 0); /* wait vref ready */

	R_DAC_CHA_CTRL |= (DAC_CH_EN|DAC_SIGNED); /* enable CHA and use signed data type */

	R_DAC_CHA_FIFO |= DAC_FIFO_RST; /* reset FIFO */
	R_DAC_CHB_FIFO |= DAC_FIFO_RST;

	//R_DAC_IISEN |= DAC_IIS_EN; /* enable IIS */

	R_DAC_CHA_CTRL|= 0xf;		//set OPA max 0xf
	R_DAC_CHB_CTRL|= 0xf;

	vic_irq_register(VIC_AUDA, dac_cha_fifo_isr);	/* register DAC isr */
	vic_irq_enable(VIC_AUDA);

	dac_fifo_level_set(0x8,0x8);
	dma_struct_dbf.channel = 0xff;
}

void dac_fifo_level_set(INT8U cha_level, INT8U chb_level)
{
	R_DAC_CHA_FIFO &= ~DAC_FIFO_LEVEL;
	R_DAC_CHB_FIFO &= ~DAC_FIFO_LEVEL;

	R_DAC_CHA_FIFO |= (cha_level << 4);
	R_DAC_CHB_FIFO |= (cha_level << 4);
	cha_int_level = cha_level;
	chb_int_level = chb_level;
}

void dac_data_signed_set(BOOLEAN flag)
{
	if(flag == TRUE)
	{
		R_DAC_CHA_CTRL |= DAC_SIGNED;
	}
	else
	{
		R_DAC_CHA_CTRL &= ~DAC_SIGNED;
	}
}

void dac_mono_set(void)
{
	R_DAC_CHB_CTRL |= DAC_MONO;
	R_DAC_CHB_CTRL |= DAC_CHB_CHACFG;
}

/*left right left..data in buffer */
void dac_stereo_set(void)
{
	R_DAC_CHB_CTRL &= ~DAC_MONO;
	R_DAC_CHB_CTRL |= DAC_CHB_CHACFG;
}

/*place left in one buffer and right in one buffer
  you have to send data to channel A and channel B individually */
void dac_stereo_indi_buffer_set(void)
{
	R_DAC_CHB_CTRL &= ~DAC_MONO;
	R_DAC_CHB_CTRL &= ~DAC_CHB_CHACFG;
}

#define TIMER_PRELOAD_COUNTER_SET(TIMERID, START_COUNT) DRV_Reg32(TIMERA_PRELOAD_ADDR + EACH_REG_OFFSET * 8 * TIMERID) = (START_COUNT & GET16BITS_MASK);	//R_TIMERA_PRELOAD &= (start_count & GET16BITS_MASK)
void dac_sample_rate_set(INT32U hz)
{
	//dac_timer_freq_setup(hz*2);	// josephhsieh@131122
	INT32U	freq = (0x10000 - (SYS_CLK / 2 / (hz * 2))) & 0x0000FFFF;
	R_TIMERE_PRELOAD = freq;

	R_TIMERE_CTRL |= 0x2060;
}

#define SET_TMXIE	0x00004000
#define SET_TMXEN	0x00002000
void dac_timer_stop(void)
{
	//timer_stop(TIMER_E);			// josephhsieh@121122

	R_TIMERE_CTRL &= ~(SET_TMXIE | SET_TMXEN);

}

void dac_pga_set(INT8U gain)
{
	R_DAC_PGA &= ~DAC_PGA;
	R_DAC_PGA |= gain;
}

INT8U dac_pga_get(void)
{
	INT8U	pga;

	pga = R_DAC_PGA;
	pga &= DAC_PGA;

	return pga;
}

void dac_vref_set(BOOLEAN status)
{
	if(status == TRUE)
	{	/* enable */
		R_DAC_CHA_CTRL |= DAC_VREFEN;
		while((R_DAC_PGA & DAC_MUTE) != 0);

		/* wait vref ready */
	}
	else
	{	/* disable */
		R_DAC_CHA_CTRL &= ~DAC_VREFEN;
	}
}

#if DRV_l1_DAC_ON_OFF
extern INT8U dma_to_dac_busy;
#endif

static void dac_cha_fifo_isr(void)
{
	INT8U	i = 0;
	R_DAC_CHA_CTRL |= DAC_FEM_IF;				/* clear interrupt flag */

#if DRV_l1_DAC_ON_OFF
	if(!(R_DAC_CHA_FIFO & 0xF)&&!(R_DAC_CHB_FIFO & 0xF))
	{
		R_DAC_CHA_CTRL &= ~DAC_FEM_IEN;

		if (!dma_to_dac_busy)
		{
			// Disable DAC output
			R_DAC_CHA_CTRL &= ~DAC_CH_EN;
			R_DAC_CHB_CTRL &= ~DAC_CH_EN;
		}
	}
/*
	if (!dma_to_dac_busy) {
		// Disable DAC output
		R_DAC_CHA_CTRL &= ~DAC_CH_EN;
		R_DAC_CHB_CTRL &= ~DAC_CH_EN;
	}
	*/
#else

	/* start transmition */
	for(i = 0; (i < (16 - cha_int_level)) && (dac_ctrl.cha_len > dac_ctrl.cha_count); i++)
	{
		if((!(R_DAC_CHA_FIFO & DAC_FIFO_FULL)))
		{
			R_DAC_CHA_DATA = dac_ctrl.cha_data[dac_ctrl.cha_count++];
		}
	}

	if(dac_ctrl.cha_count >= dac_ctrl.cha_len)
	{
		drv_l1_dac_int_set(DAC_CHA, DAC_DISABLED);
		*dac_ctrl.cha_notify = 1;
	}
#endif
}

static void dac_chb_fifo_isr(void)
{
	INT8U	i = 0;
	R_DAC_CHB_CTRL |= DAC_FEM_IF;				/* clear interrupt flag */

	/* start transmition */
	for(i = 0; (i < (16 - chb_int_level)) && (dac_ctrl.chb_len > dac_ctrl.chb_count); i++)
	{
		if((!(R_DAC_CHB_FIFO & DAC_FIFO_FULL)))
		{
			R_DAC_CHB_DATA = dac_ctrl.chb_data[dac_ctrl.chb_count++];
		}
	}

	if(dac_ctrl.chb_count >= dac_ctrl.chb_len)
	{
		drv_l1_dac_int_set(DAC_CHB, DAC_DISABLED);
		*dac_ctrl.chb_notify = 1;
	}
}

void dac_cha_data_put(INT16S *data, INT32U len, INT8S *notify)
{
	dac_ctrl.cha_data = data;
	dac_ctrl.cha_len = len;
	dac_ctrl.cha_count = 0;
	dac_ctrl.cha_notify = notify;

	if(len != 0)
	{
		drv_l1_dac_int_set(DAC_CHA, DAC_ENABLED);
	}
}

void dac_chb_data_put(INT16S *data, INT32U len, INT8S *notify)
{
	dac_ctrl.chb_data = data;
	dac_ctrl.chb_len = len;
	dac_ctrl.chb_count = 0;
	dac_ctrl.chb_notify = notify;

	if(len != 0)
	{
		drv_l1_dac_int_set(DAC_CHB, DAC_ENABLED);
	}
}

INT32S dac_cha_data_dma_put(INT16S *data, INT32U len, INT8S *notify)
{
	DMA_STRUCT	dma_struct;
	INT32S		status;

	*notify = C_DMA_STATUS_WAITING;
	dma_struct.s_addr = (INT32U) data;
	dma_struct.t_addr = (INT32U) P_DAC_CHA_DATA;
	dma_struct.width = DMA_DATA_WIDTH_2BYTE;	// DMA_DATA_WIDTH_1BYTE or DMA_DATA_WIDTH_2BYTE or DMA_DATA_WIDTH_4BYTE
	dma_struct.count = (INT32U) len;
	dma_struct.notify = notify;
	dma_struct.timeout = 0;
	status = drv_l1_dma_transfer(&dma_struct);
	if(status != 0)
		return status;

	return STATUS_OK;
}

INT32S dac_chb_data_dma_put(INT16S *data, INT32U len, INT8S *notify)
{
	DMA_STRUCT	dma_struct;
	INT32S		status;

	*notify = C_DMA_STATUS_WAITING;
	dma_struct.s_addr = (INT32U) data;
	dma_struct.t_addr = (INT32U) P_DAC_CHB_DATA;
	dma_struct.width = DMA_DATA_WIDTH_2BYTE;	// DMA_DATA_WIDTH_1BYTE or DMA_DATA_WIDTH_2BYTE or DMA_DATA_WIDTH_4BYTE
	dma_struct.count = (INT32U) len;
	dma_struct.notify = notify;
	dma_struct.timeout = 0;
	status = drv_l1_dma_transfer(&dma_struct);
	if(status != 0)
		return status;

	return STATUS_OK;
}

#if _OPERATING_SYSTEM == _OS_UCOS2

/* use DMA double buffer */
INT32S dac_cha_dbf_put(INT16S *data, INT32U len, OS_EVENT *os_q)
{
	INT32S	status;

	dma_struct_dbf.s_addr = (INT32U) data;
	dma_struct_dbf.t_addr = (INT32U) P_DAC_CHA_DATA;
	dma_struct_dbf.width = DMA_DATA_WIDTH_2BYTE;	// DMA_DATA_WIDTH_1BYTE or DMA_DATA_WIDTH_2BYTE or DMA_DATA_WIDTH_4BYTE
	dma_struct_dbf.count = (INT32U) len;
	dma_struct_dbf.notify = NULL;
	dma_struct_dbf.timeout = 0;

	status = drv_l1_dma_transfer_with_double_buf(&dma_struct_dbf, os_q);
	if(status != 0)
	{
		return status;
	}

	++fps_audio_dac_dbf_start;
	return STATUS_OK;
}

/* set second buffer for DMA double buffer */
INT32S dac_cha_dbf_set(INT16S *data, INT32U len)
{
	INT32S	status;

	dma_struct_dbf.s_addr = (INT32U) data;
	dma_struct_dbf.count = (INT32U) len;
	dma_struct_dbf.notify = NULL;
	dma_struct_dbf.timeout = 0;

	status = drv_l1_dma_transfer_double_buf_set(&dma_struct_dbf);
	if(status != 0)
	{
		return status;
	}

	return STATUS_OK;
}

INT32S dac_dbf_status_get(void)
{
	if(dma_struct_dbf.channel == 0xff)
	{
		return -1;
	}

	return drv_l1_dma_dbf_status_get(dma_struct_dbf.channel);
}

INT32S dac_dma_status_get(void)
{
	if(dma_struct_dbf.channel == 0xff)
	{
		return -1;
	}

	return drv_l1_dma_status_get(dma_struct_dbf.channel);
}

void dac_dbf_free(void)
{
	drv_l1_dma_transfer_double_buf_free(&dma_struct_dbf);
	dma_struct_dbf.channel = 0xff;
}

#endif

//=== This is for code configuration DON'T REMOVE or MODIFY it ===//
#endif //(defined _DRV_L1_DAC) && (_DRV_L1_DAC == 1)              //

//================================================================//
